import { PlayUser } from './PlayUser';
import { UserAttribute } from './UserAttribute';
import { BallColor, BallAttribute } from './BallAttribute';
import { randomGenGameId, randomGenRoomId } from './CommUtil';
import { GameOutLookAtrribute } from './OutLookAttribute';
// 当前击球的属性
export class HitBallAttribute {
  public step: number = 0;
  public userId: string = ''; // 是谁打进球的
  public ba: BallAttribute = null;
  constructor(step: number, ba: BallAttribute, uid: string) {
    this.step = step;
    this.ba = ba;
    this.userId = uid;
  }
}
// 0 默认值 1 已调整完方向 2 已出杆 3 全部球已经停止运动
export class ActionStage {
  public static InitStage = 0; // 初始状态
  public static DirectionFinished = 1; // 方向瞄准结束
  public static ForceFinished = 2;  // 力度击球结束
  public static CoolingPeriod = 3; //  冷静期
  public static WaitConfirm = 4;   //  等待确认
  private stage = 0;
  private coolingTime = 0;
  constructor(s: number = 0) {
    this.stage = s;
  }
  isInitStage() { return this.stage === ActionStage.InitStage; }
  isDirectionFinished() { return this.stage === ActionStage.DirectionFinished; }
  isForceFinished() { return this.stage === ActionStage.ForceFinished; }
  isCoolingPeriod() { return this.stage === ActionStage.CoolingPeriod; }
  isWaitConfrim() { return this.stage === ActionStage.WaitConfirm; }

  initStage() { this.stage = ActionStage.InitStage; }
  directionFinished() { this.stage = ActionStage.DirectionFinished; }
  forceFinished() { this.stage = ActionStage.ForceFinished; }
  coolingPeriod() { this.stage = ActionStage.CoolingPeriod; this.coolingTime = new Date().getTime();}
  waitConfirm() { this.stage = ActionStage.WaitConfirm; }
  coolingFinished(isAllStopped: boolean) {
    var interval = 1500;
    if (!isAllStopped) interval = 10000;
    const nt = new Date().getTime();
    if (nt > this.coolingTime + interval) {
      return true;
    }
    return false;
  }
  isCostTimeStage() {
    return this.isInitStage() || this.isDirectionFinished();
  }
  public canForceHit() {
    return this.isInitStage() || this.isDirectionFinished();
  }
  public hasHitMainBall() {
    return this.isForceFinished() || this.isCoolingPeriod() || this.isWaitConfrim();
  }
  public canForwardGame() {
    return this.isWaitConfrim() || this.isCoolingPeriod() || this.isInitStage();
  }

  public isSyncFrameStage() {
    return this.isForceFinished() || this.isCoolingPeriod();
  }
  public isSimuFrameStage() {
    return this.isForceFinished() || this.isCoolingPeriod();
  }
  public isExceptionStage() {
    return this.isInitStage() || this.isDirectionFinished() || this.isWaitConfrim();
  }
}

// 对战类型： 网络排位对战，好友对战， 花式闯关，无尽模式
export class BattleCate {
  public static Net = 0;
  public static Friend = 1;
  public static Fancy = 2;
  public static Endless = 3;
  private battle = 0;
  constructor(b: number) {
    this.battle = b;
  }
  isNetBattle() { return this.battle === BattleCate.Net; }
  isFrientBattle() { return this.battle === BattleCate.Friend; }
  isFancyBattle() { return this.battle === BattleCate.Fancy; }
  isEndlessBattle() { return this.battle === BattleCate.Endless;}

  netBattle() { this.battle = BattleCate.Net; }
  frientBattle() { this.battle = BattleCate.Friend; }
  fancyBattle() { this.battle = BattleCate.Fancy; }
  endlessBattle() { this.battle = BattleCate.Endless; }

  valueBattle() { return this.battle; }
}

// 游戏类型： 8球 9球 斯诺克 决定游戏规则
export class GameType {
  public static EightBall = 0;
  public static Snooker = 1;
  public static NineBall = 2;
  private type = 0;
  constructor(t: number) {
    this.type = t;
  }
  isEightBall() { return this.type === GameType.EightBall; }
  isSnooker() { return this.type === GameType.Snooker; }
  isNineBall() { return this.type === GameType.NineBall; }

  eightBall() { this.type = GameType.EightBall; }
  snooker() { this.type = GameType.Snooker; }
  nineBall() { this.type = GameType.NineBall; }

  setValueType(tp: number) {
    this.type = tp;
  }
  valueType() {
    return this.type;
  }
}

// 游戏状态
export class GameStatus {
  public static Inited = 0;
  public static Confirmed = 1;
  public static Started = 2;
  public static Ended = 3;
  private status = 0;
  constructor(s: number) {
    this.status = s;
  }
  isInited() { return this.status === GameStatus.Inited; }
  isConfirmed() { return this.status === GameStatus.Confirmed; }
  isStarted() { return this.status === GameStatus.Started; }
  isEnded() { return this.status === GameStatus.Ended; }

  inited() { this.status = GameStatus.Inited; }
  confirmed() { this.status = GameStatus.Confirmed; }
  started() { this.status = GameStatus.Started; }
  ended() { this.status = GameStatus.Ended; }
}

// 游戏结束原因
export class EndReason {
  public static InitDefault = 0; // 未结束
  public static Finished = 1; // 正常结束，打进黑八/打进最后一球
  public static Surrender = 2; // 投降认输
  public static Timeout = 3;  // 超时认输
  public static BlackBallHoledException = 4;  // 黑八进洞告负，当前用户异常打进最终目标球导致游戏结束
  public static IllegalLose = 5; // 用户违规失败
  public userId = '';
  public reason = 0;

  constructor(uid: string, r: number) {
    this.userId = uid;
    this.reason = r;
  }
  setEndReason(uid: string, r: number) {
    this.userId = uid;
    this.reason = r;
  }
  inited() {
    this.userId = '';
    this.reason = EndReason.InitDefault;
  }
  win(uid: string) {
    this.userId = uid;
    this.reason = EndReason.Finished;
  }
  surrender(uid: string) {
    this.reason = EndReason.Surrender;
    this.userId = uid;
  }
  timeout(uid: string) {
    this.reason = EndReason.Timeout;
    this.userId = uid;
  }
  isSurrender() {
    return this.reason === EndReason.Surrender;
  }
  isTimeout() {
    return this.reason === EndReason.Timeout;
  }
  blackBallExceptionHoled(uid: string) {
    this.userId = uid;
    this.reason = EndReason.BlackBallHoledException;
  }
  illegalLose(uid: string) {
    this.userId = uid;
    this.reason = EndReason.IllegalLose;
  }
  isUserWin(uid: string) {
    if (uid === this.userId && this.reason === EndReason.Finished) return true;
    if (uid !== this.userId && (
      this.reason === EndReason.Surrender || 
      this.reason === EndReason.Timeout ||
      this.reason === EndReason.BlackBallHoledException ||
      this.reason === EndReason.IllegalLose
    )) {
      return true;
    }
    return false;
  }
}

export class Game {
  public roomId = '';
  public gameId = '';
  public battleCate = new BattleCate(0);
  public gameType = new GameType(0);
  public side = 0;  // 0 是自己 1 对手
  public hasSureColor = false; // 是否已经确定用户击球的花色
  public playUsers = [
    PlayUser.newHumanUser(0, new UserAttribute('123456', '????')),
    PlayUser.newRobotUser(0, new UserAttribute('985632', '????')),
  ];
  public status = new GameStatus(0);
  public endReason = new EndReason('', 0);
  // 进球列表
  public hitBallList = new Array<HitBallAttribute>;
  public currentStep: number = 0;
  public outlookAttribute: GameOutLookAtrribute = new GameOutLookAtrribute("DefaultTable", "DefaultMainBall", "DefaultRoom");
  private static instance = null as unknown as Game;
  public static Instance() {
    if (!this.instance) {
      this.instance = new Game();
    }
    return this.instance;
  }

  constructor() {
    this.roomId = randomGenRoomId();
    this.gameId = randomGenGameId();
  }
  public setSide(sd: number) { this.side = sd; }
  public randomSetSide() {
    const side = Math.random() > 0.5 ?  1: 0;
    this.side = side;
  }
  public setRoomId(roomId: string) { this.roomId = roomId; }
  public resetRoomId() { this.roomId = randomGenRoomId(); }
  
  public initNetBattle() {
    this.battleCate.netBattle();
  }
  public initFriendBattle() {
    this.battleCate.frientBattle();
  }
  public initEightBall() {
    this.gameType.eightBall();
    this.hasSureColor = false;
  }
  public initNineBall() {
    this.gameType.nineBall();
    this.hasSureColor = true;
  }
  public initSnookerBall() {
    this.gameType.snooker();
    this.hasSureColor = true;
  }
  public isSelftHitBall() {
    return this.side === 0;
  }
  public startGame() {
    this.currentStep = 0;
    this.hasSureColor = false;
    this.playUsers[0].initGame();
    this.playUsers[1].initGame();
    if (this.battleCate.isFancyBattle()) {
      this.playUsers[0].initAnyColor();
      this.hasSureColor = true;
      this.side = 0;
    } else if (this.battleCate.isEndlessBattle()) {
      this.playUsers[0].initAnyColor();
      this.hasSureColor = true;
      this.side = 0;
    } else if (this.gameType.isSnooker()) {
      this.playUsers[0].initRedColor();
      this.playUsers[1].initRedColor();
      this.hasSureColor = true;
      if (!this.battleCate.isFrientBattle()) {
        this.side = Math.floor(Math.random() * 2);
      }
    } else if (this.gameType.isNineBall()) {
      this.playUsers[0].initSpecialColor();
      this.playUsers[1].initSpecialColor();
      this.hasSureColor = true;
      if (!this.battleCate.isFrientBattle()) {
        this.side = Math.floor(Math.random() * 2);
      }
    } else if (this.gameType.isEightBall()){
      if (!this.battleCate.isFrientBattle()) {
        this.side = Math.floor(Math.random() * 2);
      }
    }
    
    this.endReason.inited();
    this.status.started();
    this.playUsers[0].score = 0;
    this.playUsers[1].score = 0;
    this.hitBallList.length = 0;
  }
  public startStep() {
    this.currentStep += 1;
    this.resetCurrentUserCostTime();
  }

  public exchangeUser() {
    if (this.battleCate.isEndlessBattle()) {
      return;
    }
    if (this.battleCate.isFancyBattle()) {
      return;
    }
    this.side = 1 - this.side;
  }

  public isRobotHitting() {
    return this.playUsers[this.side].isRobot();
  }

  public isAllUserPrepared() {
    return this.playUsers[0].isPreparedStatus() && this.playUsers[1].isPreparedStatus();
  }

  public updateCurrentUserScore(sc: number) {
    this.playUsers[this.side].score += sc;
  }
  // 惩罚当前用户， 给对手加分
  public penaltyCurrentUserScore(sc: number) {
    this.playUsers[1 - this.side].score += sc;
  }
  public setCurrentUserColor(clr: number) {
    this.hasSureColor = true;
    this.playUsers[this.side].color.color = clr;
    this.playUsers[1 - this.side].color.color = BallColor.oppoColor(clr);
  }
  public setUserSideBlackColor(sd: number) {
    this.playUsers[sd].color.color = BallColor.BlackBallEight;
  }
  public getPlayUserIndex(index: number) {
    return this.playUsers[index];
  }
  public getCurrentPlayUserColor() {
    return this.playUsers[this.side].color;
  }
  public currentUserWin() {
    this.endReason.win(this.playUsers[this.side].userAttribute.userId);
    this.commonEndGame();
  }
  public currentUserLose() {
    this.endReason.win(this.playUsers[1 - this.side].userAttribute.userId);
    this.commonEndGame();
  }
  public currentUserTimeout() {
    this.endReason.timeout(this.playUsers[this.side].userAttribute.userId);
    this.commonEndGame();
  }
  public currentUserBlackBallExceptionHoled() {
    this.endReason.blackBallExceptionHoled(this.playUsers[this.side].userAttribute.userId);
    this.commonEndGame();
  }

  public selfSurrender() {
    this.endReason.surrender(this.playUsers[0].userAttribute.userId);
    this.commonEndGame();
  }
  public robotSurrender() {
    this.endReason.surrender(this.playUsers[1].userAttribute.userId);
    this.commonEndGame();
  }

  public endGame(userId: string, reason: number) {
    this.endReason.setEndReason(userId, reason);
    this.commonEndGame();
  }

  public isOppoUserLeaved() {
    return this.playUsers[1].isLeavedStatus();
  }
  public resetCurrentUserCostTime() {
    this.playUsers[this.side].restartStepTime();
  }
  public getCurrentUserCostTime() {
    const t = (new Date()).valueOf();
    return Math.floor((t - this.playUsers[this.side].startStepTime) / 1000);
  }
  public getCurrentUserAimLine() {
    return this.playUsers[this.side].outlookAttribute.aimLineLength();
  }
  public getCurrentUserQiuGanName() {
    return this.playUsers[this.side].outlookAttribute.qiuGan.ganName;
  }
  public setSelfUserQiuGanName(name: string) {
    return this.playUsers[0].outlookAttribute.updateGanName(name);
  }
  public setOutlookAttribute(tableName: string, mainBallName: string, roomName: string) {
    this.outlookAttribute.updateTableName(tableName);
    this.outlookAttribute.updateMainBallName(mainBallName);
    this.outlookAttribute.updateRoomName(roomName);
  }
  public isIgnoreColorStage() {
    return this.playUsers[0].color.isSpecialColor() || this.playUsers[1].color.isSpecialColor();
  }
  public setSelfUserAttribute(ua: UserAttribute) {
    this.playUsers[0].userAttribute = ua;
  }
  public setOppoPlayUser(st: number, ua: UserAttribute) {
    this.playUsers[1].status = st;
    this.playUsers[1].userAttribute = ua;
  }
  public setOppoUserAttribute(ua: UserAttribute) {
    this.playUsers[1].userAttribute = ua;
  }
  public setOppoUserStatus(st: number) {
    this.playUsers[1].status = st;
  }

  public currentUserId() {
    return this.playUsers[this.side].userAttribute.userId;
  }

  public getOppoUserName() {
    return this.playUsers[1].userAttribute.nickName;
  }

  public isSelfWin() {
    return this.endReason.isUserWin(this.playUsers[0].userAttribute.userId);
  }
  public allUserStart() {
    this.playUsers[0].started();
    this.playUsers[1].started();
    this.status.started();
  }
  public websocketHeader() {
    return {
      roomId: this.roomId,
      gameId: this.gameId,
      gameType: this.gameType.valueType(),
      battleCate: this.battleCate.valueBattle(),
    }
  }
  public pushHitBallList(ballName: string) {
    const step = this.currentStep;
    const ba = BallAttribute.getBallByName(ballName);
    const hba = new HitBallAttribute(step, ba, Game.Instance().currentUserId())
    this.hitBallList.push(hba);
  }
  public checkHitBallList(step: number, hitBallListName: string[]) {
    // 先清理当前step的数据，然后一个一个添加
    this.hitBallList = this.hitBallList.filter(hba => hba.step !== step);

    for (const bname of hitBallListName) {
      this.pushHitBallList(bname);
    }
  }
  private commonEndGame() {
    this.status.ended();
    this.playUsers[0].ended();
    this.playUsers[1].ended();
  }
}

export class AimType {
  public static Empty = 0; // 瞄准一个随机的点
  public static TargetBall = 1; // 瞄准的是目标球
  public static TargetHole = 2;  // 瞄准的是球洞
  public static DeskEdge = 3; // 瞄准的是桌边
  private aimType: number = 0;
  public aimEmpty() { this.aimType = AimType.Empty; }
  public aimTargetBall() { this.aimType = AimType.TargetBall; }
  public aimTargetHole() { this.aimType = AimType.TargetHole; }
  public aimDeskEdge() { this.aimType = AimType.DeskEdge; }
  public isAimEmpty() { return this.aimType === AimType.Empty; }
  public isAimTargetBall() { return this.aimType === AimType.TargetBall; }
  public isAimTargetHole() { return this.aimType === AimType.TargetHole; }
  public isAimDeskEdge() { return this.aimType === AimType.DeskEdge; }
}